﻿#pragma once

#include "MapObject.h"
#include "../../Shared/Enums.h"
#include "../MirDatabase/NPCInfo.h"
#include "ConquestObject.h"
#include "../MirDatabase/QuestInfo.h"
#include "../../Shared/Data/ItemData.h"
#include "DelayedAction.h"
#include "PlayerObject.h"
#include "MonsterObject.h"
#include "../../Shared/Data/Stat.h"
#include "../../Shared/Packet.h"
#include "../MirEnvir/Envir.h"
#include <string>
#include <unordered_map>
#include <vector>
#include <algorithm>
#include <cmath>
#include "exceptionhelper.h"
#include "../../stringhelper.h"

//C# TO C++ CONVERTER NOTE: Forward class declarations:
namespace Server::MirObjects { class NPCSpeech; }

using namespace Server::MirDatabase;
using namespace Server::MirEnvir;
using namespace Server::MirObjects;
namespace S = ServerPackets;

namespace Server::MirObjects
{
    class NPCObject final : public MapObject
    {
    private:
        int ScriptID = 0;
        int CurrentMapIndex = 0;
        MirDirection Direction = static_cast<MirDirection>(0);

    public:
        virtual ~NPCObject()
        {
            delete Info;
            delete Conq;
        }

        ObjectType getRace() const override;

        static NPCObject *Get(unsigned int objectID);

        int getScriptID() const;
        void setScriptID(int value);

        NPCInfo *Info;
    private:
        static constexpr long long TurnDelay = 10000, SpeechDelay = 5000;
    public:
        long long TurnTime = 0, UsedGoodsTime = 0, VisTime = 0, SpeechTime = 0;
        bool Visible = true;

        std::unordered_map<int, bool> VisibleLog = std::unordered_map<int, bool>();

        ConquestObject *Conq;
        std::vector<QuestInfo*> Quests = std::vector<QuestInfo*>();
        std::vector<NPCSpeech*> Speech = std::vector<NPCSpeech*>();

        std::vector<UserItem*> UsedGoods = std::vector<UserItem*>();
        std::unordered_map<std::string, std::vector<UserItem*>> BuyBack = std::unordered_map<std::string, std::vector<UserItem*>>();

        bool NeedSave = false;

        NPCObject(NPCInfo *info);

    private:
        void LoadScript();

    public:
        void ProcessGoods(bool clear = false);


        #pragma region Overrides
        void Process(DelayedAction *action) override;

        bool IsAttackTarget(PlayerObject *attacker) override;
        bool IsFriendlyTarget(PlayerObject *ally) override;
        bool IsFriendlyTarget(MonsterObject *ally) override;
        bool IsAttackTarget(MonsterObject *attacker) override;

        Buff *AddBuff(BuffType type, MapObject *owner, int duration, Stats *stat, bool visible = false, bool infinite = false, bool stackable = false, bool refreshStats = true, std::vector<int> &values) override;

        int Attacked(PlayerObject *attacker, int damage, DefenceType type = DefenceType::ACAgility, bool damageWeapon = true) override;

        int Attacked(MonsterObject *attacker, int damage, DefenceType type = DefenceType::ACAgility) override;

        int Struck(int damage, DefenceType type = DefenceType::ACAgility) override;

        void SendHealth(PlayerObject *player) override;

        void Die() override;

        int Pushed(MapObject *pusher, MirDirection dir, int distance) override;

        unsigned short getLevel() const override;
        void setLevel(unsigned short value) override;

        void ReceiveChat(const std::string &text, ChatType type) override;

        void Turn(MirDirection dir);

        void Process() override;

        void SetOperateTime() override;

        void Hide();

        void Show();

        bool FindNearby(int distance);

        Packet *GetInfo() override;

        Packet *GetUpdateInfo();

        void ApplyPoison(Poison *p, MapObject *Caster = nullptr, bool NoResist = false, bool ignoreDefence = true) override;

        std::string getName() const override;
        void setName(const std::string &value) override;

        bool getBlocking() const override;


        void CheckVisible(PlayerObject *Player, bool Force = false);

        int getCurrentMapIndex() const override;
        void setCurrentMapIndex(int value) override;

        Point *getCurrentLocation() const override;
        void setCurrentLocation(Point *value) override;

        MirDirection getDirection() const override;
        void setDirection(MirDirection value) override;

        int getHealth() const override;

        int getMaxHealth() const override;
        #pragma endregion

    };

    class NPCSpeech
    {
    private:
        int Weight = 0;
        std::string Message;

    public:
        int getWeight() const;
        void setWeight(int value);
        std::string getMessage() const;
        void setMessage(const std::string &value);

        int GetWeight(RandomProvider *rnd, int max);
    };
}
